package clang.control;

import java.util.HashMap;
import java.util.Vector;

import clang.data.VarName;
import clang.data.Variable;
import clang.functions.FunctionCall;

public class CodeBlock {

	private CodeBlock parent = null;
	private Vector<Variable> localVars = new Vector<Variable>();
	private HashMap<String, Variable> varMap = new HashMap<String, Variable>();
	
	private Vector<CodeElement> content = new Vector<CodeElement>();
	private Vector<VarName> cachedReferences = new Vector<VarName>();
	private Vector<FunctionCall> cachedCalls = new Vector<FunctionCall>();
	
	private Variable retValue = null;
	private int ip = 0;
	
	public CodeBlock(CodeBlock parent)
	{
		this.parent = parent;
	}
	
	public Variable execute() throws Exception
	{
		return execute(new Vector<Variable>());
	}
	
	public Variable execute(Vector<Variable> params) throws Exception
	{
		localVars.clear();
		varMap.clear();
		cachedReferences.clear();
		retValue = null;
		
		for(Variable v : params)
		{
			localVars.add(v);
			varMap.put(v.getName(), v);
		}
		
		ip = 0;
		
		ExecutionThread.get().pushBlock(this);
		while(ip < content.size())
		{
			CodeElement elem = content.get(ip);
			
			//try {
				elem.execute();
			//} catch (Exception e) {
				//throw new Exception("Runtime error at ip " + ip + " : " + e.getMessage());
			//}
			
			ip++;
		}
		
		for(Variable v : localVars)
		{
			v.erase();
		}
		
		localVars.clear();
		varMap.clear();
		
		for(VarName vn : cachedReferences)
			vn.deref();
		
		for(FunctionCall fc : cachedCalls)
			fc.invalidate();
		
		ExecutionThread.get().popBlock(this);
		return retValue;
	}
	
	public void endBlock(Variable returnValue)
	{
		retValue = returnValue;
		ip = content.size();
	}
	
	public void append(CodeElement element)
	{
		content.add(element);
	}
	
	public void defineVariable(Variable var)
	{
		localVars.add(var);
		varMap.put(var.getName(), var);
	}
	
	public Variable findVar(VarName name) throws Exception
	{
		Variable v = varMap.get(name.getName());
		if(v != null)
			return v;
		
		if(parent != null)
			return parent.findVar(name);
		else
			throw new Exception("Variable not found : " + name.getName());
	}
	
	public void addCachedReference(VarName vn)
	{
		cachedReferences.add(vn);
	}
	
	public void addCachedCall(FunctionCall fc)
	{
		cachedCalls.add(fc);
	}
}
